package com.molichuxing.services.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.molichuxing.framework.contants.RedisKeyConst;
import com.molichuxing.framework.utils.Md5Util;
import com.molichuxing.framework.utils.RedisUtil;
import com.molichuxing.framework.utils.StringUtil;
import com.molichuxing.services.infrastructure.dto.response.IovResponseDto;
import org.apache.commons.collections.map.HashedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


@Component
public class IovUtils {
    private static Logger logger = LoggerFactory.getLogger(IovUtils.class);

    private String appid;
    private String secret;
    private String grantType;
    private String key;
    private Integer expire;

    @Resource
    private RedisUtil redisUtil;

    //请求
    public static Map<String, String> getHeaderMap = new HashMap<>(); //get请求，请求头参数
    public static Map<String, String> postHeaderMap = new HashMap<>(); //post请求，请求头参数

    static {
        postHeaderMap.put("Content-Type", "application/json; charset=UTF-8");
    }

    /**
     * 为请求头设置token
     *
     * @param token
     */
    public static void setTokenToHeader(String token) {
        getHeaderMap.put("access_token", token);
        postHeaderMap.put("access_token", token);
    }


    public static IovResponseDto doGet(String url, Map<String, String> paramMap) throws IOException {
        logger.info("op调用车联网接口，接口地址 :: {}", url);
        String iovResponseStr = HttpUtil.doGet(url, getHeaderMap, paramMap);
        return parseResult(iovResponseStr);
    }

    public static IovResponseDto doPost(String url, Map<String, Object> paramMap) throws IOException {
        logger.info("op调用车联网接口，接口地址 :: {}", url);
        String iovResponseStr = HttpUtil.doPost(url, postHeaderMap, paramMap);
        return parseResult(iovResponseStr);
    }

    private static IovResponseDto parseResult(String iovResponseStr) {
        logger.info("op调用车联网接口，车联网返回值，iovResponse :: {}", iovResponseStr);
        if (StringUtil.isEmpty(iovResponseStr)) {
            throw new NullPointerException("result is null");
        }

        IovResponseDto iovResponse = JSON.toJavaObject(JSONObject.parseObject(iovResponseStr), IovResponseDto.class);
        return iovResponse;
    }


    /**
     * 获得token，如果没有则set再get
     *
     * @return
     */
    public String getSet() {
        String token = getToken();
        if (StringUtil.isEmpty(token)) {
            setToken();
            token = getToken();
        }
        return token;
    }

    /**
     * 获得token，先set再get
     *
     * @return
     */
    public String setGet() {
        setToken();
        return getToken();
    }

    /**
     * 获取token
     *
     * @return
     */
    public String getToken() {
        long overTime = System.currentTimeMillis() + 3000L;
        while (System.currentTimeMillis() < overTime && redisUtil.exists(RedisKeyConst.TOKEN_SEND_IOV_LOCK_KEY)) {
        } //如果key是否存在的话，证明有线程持有锁，超时时间定3秒，自选等待释放锁或者超时

        String redisKey = RedisKeyConst.TOKEN_SEND_IOV_KEY + Md5Util.md5Hex(Base64Utils.encodeToString(key.getBytes()));
        return redisUtil.get(redisKey);
    }

    /**
     * 设置token
     *
     * @return
     */
    public boolean setToken() {
        String lockValue = UUID.randomUUID().toString();
        long lockResult = redisUtil.setnx(RedisKeyConst.TOKEN_SEND_IOV_LOCK_KEY, lockValue, 10); //redis分布式锁，并发情况下智能有一个线程获取token
        if (lockResult == 1l) {
            Map<String, String> paramMap = new HashedMap();
            paramMap.put("appid", appid);
            paramMap.put("secret", secret);
            paramMap.put("grant_type", grantType);
            try {
                String tokenText = HttpUtil.doGet(IovPropertyUtils.GET_TOKEN_URL, paramMap);
                logger.error("获取车联网接口访问token，车联网返回值为， str ::{}", tokenText);
                if (StringUtil.isEmpty(tokenText)) {
                    return false;
                }

                JSONObject tokenJson = JSONObject.parseObject(tokenText);
                if (tokenJson == null) {
                    return false;
                }

                Integer code = tokenJson.getInteger("code");
                if (code == null || code != 0) {
                    return false;
                }

                JSONObject data = tokenJson.getJSONObject("data");
                if (data == null) {
                    return false;
                }

                String token = data.getString("access_token");
                int expires = data.getInteger("expires_in") / 2;


                String redisKey = RedisKeyConst.TOKEN_SEND_IOV_KEY + Md5Util.md5Hex(Base64Utils.encodeToString(key.getBytes()));
                redisUtil.setex(redisKey, token, expires);

                //释放redis分布式锁
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                Long result = redisUtil.eval(script, Collections.singletonList(RedisKeyConst.TOKEN_SEND_IOV_LOCK_KEY), Collections.singletonList(lockValue));
                if (result.equals(1L)) {
                    return true;
                } else {
                    return false;
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        } else {
            return true;
        }
        return false;
    }


    @Value("${iov.permission.appid}")
    public void setAppid(String appid) {
        this.appid = appid;
    }

    @Value("${iov.permission.secret}")
    public void setSecret(String secret) {
        this.secret = secret;
    }

    @Value("${iov.permission.grant_type}")
    public void setGrantType(String grantType) {
        this.grantType = grantType;
    }

    @Value("${iov.permission.key}")
    public void setKey(String key) {
        this.key = key;
    }

    @Value("${iov.permission.token.expire}")
    public void setExpire(Integer expire) {
        this.expire = expire;
    }
}
